/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 *   Netscape Communications Corporation.
 *
 * Portions created by the Initial Developer are Copyright (C) 2001
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Joe Hewitt <hewitt@netscape.com> (original author)
 *   George Bradt
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

var EXPORTED_SYMBOLS = ["eventLogTreeJsm"];

var eventLogTreeJsm = {

	// This module was created for Event Spy 0.4.
    
	// This function was taken from DOM Inspector 2.0.9 (jsObjectViewer.js).
	// Window references are passed in to allow use of document.
	// Used in: events.js, eventLogTree.jsm.

    addTreeItem: function (aTreeChildren, aName, aValue, aObject, aWindow) {
        var ti = aWindow.document.createElement("treeitem");
        ti.__JSObject__ = aObject;
        ti.__JSValue__ = aValue;
    
        var value;
        
        if (aValue === null) {
          value = "(null)";
        }
        else if (aValue === undefined) {
          value = "(undefined)";
        }
        else {
          try {
            value = aValue.toString();
            value = value.replace(/\n|\r|\t|\v/g, " ");
          }
          catch (ex) {
            value = "";
          }
        }
    
        ti.setAttribute("typeOf", typeof(aValue));
    
        if (typeof(aValue) == "object" && aValue !== null) {
          ti.setAttribute("container", "true");
        }
        else if (typeof(aValue) == "string") {
          value = "\"" + value + "\"";
        }
    
        var tr = aWindow.document.createElement("treerow");
        ti.appendChild(tr);
    
        var tc = aWindow.document.createElement("treecell");
        tc.setAttribute("label", aName);
        tr.appendChild(tc);
        tc = aWindow.document.createElement("treecell");
        tc.setAttribute("label", value);
        
        if (aValue === null) {
          tc.setAttribute("class", "inspector-null-value-treecell");
        }
        
        tr.appendChild(tc);
        aTreeChildren.appendChild(ti);
        return ti;
    },

	// This function was taken from DOM Inspector 2.0.9 (jsObjectViewer.js).
	// Used in: events.js.

    openTreeItem: function(aItem, aWindow) {
        var treechildren = aItem.getElementsByTagName("treechildren").item(0);
        
        if (!treechildren) {
          treechildren = aWindow.document.createElement("treechildren");
          this.buildPropertyTree(treechildren, aItem.__JSValue__, aWindow);
          aItem.appendChild(treechildren);
        }
    },

	// This function was taken from DOM Inspector 2.0.9 (jsObjectViewer.js).
	// Used in: eventLogTree.jsm.

    buildPropertyTree: function(aTreeChildren, aObject, aWindow) {
        // sort the properties
        var propertyNames = [];
        
        for (let prop in aObject) {
          propertyNames.push(prop);
        }
    
       /**
        * A sorter for numeric values. Numerics come before non-numerics. If both
        * parameters are non-numeric, returns 0.
        *
        * @param a
        *        One value to compare.
        * @param b
        *        The other value to compare against a.
        * @return -1 if a should come before b, 1 if b should come before a, or 0
        *         if they are equal
        */
        function sortNumeric(a, b) {
          if (isNaN(a)) {
            return isNaN(b) ? 0 : 1;
          }
          
          if (isNaN(b)) {
            return -1;
          }
          
          return a - b;
        }
    
       /**
        * A sorter for the JavaScript object property names. Sort order: constants
        * with numeric values sorted numerically by value then alphanumerically
        * by name, all other constants sorted alphanumerically by name, non-
        * constants with numeric names sorted numerically by name (ex: array
        * indices), all other non-constants sorted alphanumerically by name.
        *
        * @param a
        *        One name to compare.
        * @param b
        *        The other name to compare against a.
        * @return -1 if a should come before b, 1 if b should come before a, 0 if
        *         they are equal
        */
        function sortFunction(a, b) {
          // assume capitalized non-numeric property names are constants
          var aIsConstant = a == a.toUpperCase() && isNaN(a);
          var bIsConstant = b == b.toUpperCase() && isNaN(b);
          
          // constants come first
          if (aIsConstant) {
            if (bIsConstant) {
              // both are constants. sort by numeric value, then non-numeric name
              return sortNumeric(aObject[a], aObject[b]) || a.localeCompare(b);
            }
            
            // a is constant, b is not
            return -1;
          }
          
          if (bIsConstant) {
            // b is constant, a is not
            return 1;
          }
          
          // neither are constants. go by numeric property name, then non-numeric
          // property name
          return sortNumeric(a, b) || a.localeCompare(b);
        }
        
        propertyNames.sort(sortFunction);
    
        // load them into the tree
        for (let i = 0; i < propertyNames.length; i++) {
          try {
            // Event Spy - removed unwrap.
            this.addTreeItem(aTreeChildren, propertyNames[i],
                             aObject[propertyNames[i]],
                             aObject, aWindow);
          }
          catch (ex) {
            // hide unsightly NOT YET IMPLEMENTED errors when accessing certain
            // properties
          }
        }
    },

	// Debug messages to console.

	debug: function(aMsg) {
		let consoleService = Components.classes["@mozilla.org/consoleservice;1"].
				getService(Components.interfaces.nsIConsoleService);
		consoleService.logStringMessage(aMsg);
	}
};